﻿using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.HttpOverrides;

namespace NoIdeaIoc;
/// <summary></summary>
public class Startup
{
    /// <summary>
    /// 根目录下的静态文件夹名
    /// </summary>
    const string staticFileDirName = "Files";
    /// <summary>
    /// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    /// </summary>
    /// <param name="app"></param>
    /// <param name="env"></param>
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        var tempPath = AppDomain.CurrentDomain.BaseDirectory + staticFileDirName;
        Directory.CreateDirectory(tempPath);
        #region 设置默认主页
        var defaultFilesOptions = new DefaultFilesOptions();
        defaultFilesOptions.DefaultFileNames.Clear();
        defaultFilesOptions.DefaultFileNames.Add("index.html");
        defaultFilesOptions.FileProvider = new WebFileProvider(tempPath);
        app.UseDefaultFiles(defaultFilesOptions);
        #endregion 设置默认主页
        #region 解决Ubuntu Nginx 代理不能获取IP问题
        app.UseForwardedHeaders(new ForwardedHeadersOptions
        {
            ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
        });
        #endregion
        app.UseStaticFiles(new StaticFileOptions()
        {
            FileProvider = new PhysicalFileProvider(AppDomain.CurrentDomain.BaseDirectory),
            ServeUnknownFileTypes = false,
            RequestPath = new PathString(""),
            ContentTypeProvider = new defaultContentTypeProvider()
        });
        app.UseExceptionHandler(new ExceptionHandlerOptions()
        {
            ExceptionHandler = new RequestDelegate(async x =>
            {
                var feature = x.Features.Get<IExceptionHandlerPathFeature>();
                x.Response.StatusCode = 500;
                x.Response.ContentType = "text/html";
                await x.Response.WriteAsync(feature.Error.Message);
            })
        });
        /*
        app.Use((context, next) =>
        {
            // 初始化用户身份
            context.User = WebExtensions.UserCredential(context);
            return next();
        });
        //*/
        app.UseHsts();
        app.UseCookiePolicy();
        app.UseCors("SignalR");
        app.UseCors("AllowSpecificorigin");
        app.UseCors("CorsPolicy");
        app.UseCors("*");
        app.UseRouting();
        app.UseAuthentication();
        app.UseAuthorization();
        //全局路由配置
        app.UseEndpoints(endpoints =>
        {
            using (var lifeTime = new LifeTime())
            {
                var configs = lifeTime.Resolve<IEnumerable<RouteConfig>>();
                int count = 0;
                if (configs != null)
                {
                    foreach (var config in configs)
                    {
                        mapControllerRoute(endpoints, config);
                        count++;
                    }
                }
                if (count < 1)
                {
                    var config = lifeTime.Resolve<RouteConfig>();
                    if (config == null) config = RouteConfig.GetDefault();
                    mapControllerRoute(endpoints, config);
                }
            }
        });
        if (_buildedCallBack != null || taskCallbacks != null)
        {
            //开始运行所有计划任务...
            ThreadPool.QueueUserWorkItem(async o =>
            {
                ILifeTime lifeTime = null;
                ILogger logger = null;
                try
                {
                    lifeTime = new LifeTime();
                    logger = lifeTime.Resolve<ILogger>();
                    if (_buildedCallBack != null)
                    {
                        _buildedCallBack(lifeTime);
                        _buildedCallBack = null;
                    }
                    if (taskCallbacks != null)
                    {
                        for (var i = 0; i < taskCallbacks.Count; i++)
                        {
                            var taskFunc = taskCallbacks[i];
                            await taskFunc(lifeTime).ConfigureAwait(false);
                        }
                        taskCallbacks = null;
                    }
                }
                catch (Exception ex)
                {
                    logger?.LogError(ex, "执行应用创建完毕时的回调发生异常");
                }
                finally
                {
                    lifeTime.Dispose();
                }
            }, this);
        }
    }
    void mapControllerRoute(IEndpointRouteBuilder endpoints, RouteConfig config)
    {
        if (config.AreaName == null || config.AreaName.Length < 1)
        {
            // 全局路由配置
            endpoints.MapControllerRoute(
               name: config.Name,
               pattern: config.Pattern,
               defaults: config.Defaults,
               constraints: config.Constraints,
               dataTokens: config.DataTokens
            );
        }
        else
        {
            endpoints.MapAreaControllerRoute(
               name: config.Name,
               areaName: config.AreaName,
               pattern: config.Pattern,
               defaults: config.Defaults,
               constraints: config.Constraints,
               dataTokens: config.DataTokens
           );
        }
    }
    static Action<ILifeTime> _buildedCallBack;
    /// <summary>
    /// 注册创建容器后的回调函数
    /// <para>创建容器后自动执行</para>
    /// </summary>
    /// <param name="action"></param>
    internal static void OnIocBuilded(Action<ILifeTime> action)
    {
        if (action == null) return;
        if (_buildedCallBack == null)
            _buildedCallBack = action;
        else
            _buildedCallBack += action;
    }
    static List<Func<ILifeTime, Task>> taskCallbacks;
    /// <summary>
    /// 注册创建容器后的回调函数
    /// <para>创建容器后自动执行</para>
    /// </summary>
    /// <param name="action"></param>
    internal static void OnIocBuilded(Func<ILifeTime, Task> action)
    {
        if (action == null) return;
        if (taskCallbacks == null) taskCallbacks = new List<Func<ILifeTime, Task>>(10);
        taskCallbacks.Add(action);
    }
    class defaultContentTypeProvider : IContentTypeProvider
    {
        public bool TryGetContentType(string subpath, [MaybeNullWhen(false)] out string contentType)
        {
            if (subpath == null || subpath.Length == 0 || subpath == "/" || subpath.EndsWith("index.html", StringComparison.OrdinalIgnoreCase))
            {
                contentType = "text/html; charset=utf-8";
                return true;
            }
            else if (subpath.EndsWith("favicon.ico", StringComparison.OrdinalIgnoreCase))
            {
                contentType = "Image/x-icon";
                return true;
            }
            else if (subpath.IndexOf($"/{staticFileDirName}/", StringComparison.OrdinalIgnoreCase) == 0)
            {
                var ext = Path.GetExtension(subpath);
                contentType = GetMimeType(ext, null, true);
            }
            else
            {
                contentType = null;
                var fileName = GetFileNameFromUrl(subpath);
                if (fileName != null && fileName.Length > 0)
                {
                    var ext = System.IO.Path.GetExtension(fileName);
                    if (ext != null && ext.Equals(".txt", StringComparison.OrdinalIgnoreCase)) contentType = "text/plain; charset=utf-8";
                }
            }
            return contentType != null;
        }
        /// <summary>
        /// 根据Url获取路径文件名
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public static string? GetFileNameFromUrl(string url)
        {
            if (url.IndexOf('%') != -1) url = System.Net.WebUtility.UrlDecode(url);
            var tempIndex = url.IndexOfAny(UrlFileSplit);
            var fileName = url;
            if (tempIndex != -1)
            {
                fileName = fileName.Substring(0, tempIndex);
            }
            tempIndex = fileName.LastIndexOf('/') + 1;
            if (tempIndex != 0) fileName = fileName.Substring(tempIndex);
            if (fileName.Length == 0) fileName = null;
            else
            {
                tempIndex = fileName.IndexOfAny(UrlFileSplit);
                if (tempIndex != -1)
                    fileName = fileName.Substring(0, tempIndex);
            }
            return fileName;
        }
        internal static readonly char[] UrlFileSplit = new char[] { '?', '&', '#' };
        /// <summary>
        /// 根据扩展名获取MimeType
        /// </summary>
        /// <param name="ext"></param>
        /// <param name="defaultMimeType"></param>
        /// <param name="isForWeb"></param>
        /// <returns></returns>
        public static string GetMimeType(string ext, string defaultMimeType = "text/pain", bool isForWeb = false)
        {
            if (ext == null || ext.Length < 1) return defaultMimeType;
            ext = ext.ToLowerInvariant();
            switch (ext)
            {
                case ".htm":
                case ".html":
                case ".shtml":
                    return isForWeb ? "text/html; charset=utf-8" : "text/html";
                case ".asc":
                case ".txt":
                case ".fnt":
                    return isForWeb ? "text/pain; charset=utf-8" : "text/plain";
                case ".json":
                    return "application/json";
                case ".tmx":
                case ".xml":
                    return isForWeb ? "application/xml; charset=utf-8" : "application/xml";
                case ".xht":
                case ".xhtml":
                    return "application/xhtml+xml";
                case ".js":
                    return isForWeb ? "application/javascript; charset=utf-8" : "application/javascript";
                case ".css":
                    return isForWeb ? "text/css; charset=utf-8" : "text/css";
                case ".php":
                case ".php3":
                case ".php4":
                case ".php5":
                case ".phtml":
                    return "application/x-httpd-php";
                case ".crt":
                case ".pem":
                    return "application/x-x509-user-cert";
                case ".vcf":
                    return "text/x-vcard";
                case ".chm":
                    return "application/mshelp";
                case ".doc":
                    return "application/msword";
                case ".docx":
                    return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
                case ".ppt":
                    return "application/vnd.ms-powerpoint";
                case ".pptx":
                    return "application/vnd.openxmlformats-officedocument.presentationml.presentation";
                case ".dotx":
                    return "application/vnd.openxmlformats-officedocument.wordprocessingml.template";
                case ".xls":
                    return "application/vnd.ms-excel";
                case ".xlsx":
                    return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
                case ".pdf":
                    return "application/pdf";
                case ".jpg":
                case ".jpeg":
                    return "Image/Jpeg";
                case ".gif":
                    return "Image/Gif";
                case ".ico":
                    return "Image/x-icon";
                case ".mac":
                    return "image/x-macpaint";
                case ".tif":
                case ".tiff":
                    return "Image/tiff";
                case ".png":
                    return "Image/Png";
                case ".webp":
                    return "Image/webp";
                case ".bmp":
                    return "Image/Bmp";
                case ".svg":
                case ".svgz":
                    return "Image/svg+xml";
                case ".swf":
                    return "application/x-shockwave-flash";
                case ".flv":
                    return "video/x-flv";
                case ".rmvb":
                    return "audio/x-pn-realaudio";
                case ".amr":
                    return "audio/amr";
                case ".au":
                    return "audio/basic";
                case ".mp3":
                    return "audio/mpeg";
                default:
                    return defaultMimeType;
            }
        }
    }
}